//
// Created by fanghr on 18-3-30.
//
#include "../include/thread_mat.h"

static void *raw_getter(void *obj);

static void *preproc(void *obj);

static void *to_gray_func(void *obj);

thread_mat::thread_mat(int dev) {
    pthread_rwlock_init(&raw.rwlock_raw, nullptr);
    pthread_rwlock_init(&after_preproc.rwlock_preproc, nullptr);
    pthread_rwlock_init(&to_gray.rwlock_to_gray, nullptr);
    raw.is_raw_data_ready = after_preproc.is_after_pre_comp_data_ready = to_gray.is_gray_mat_ready = is_run = false;
    device = dev;
}

thread_mat::~thread_mat() {
    stop();
    pthread_rwlock_destroy(&raw.rwlock_raw);
    pthread_rwlock_destroy(&after_preproc.rwlock_preproc);
    pthread_rwlock_destroy(&to_gray.rwlock_to_gray);
}

int thread_mat::run() {
    if (is_run)
        return 0;
    if (pthread_create(&raw.thread_raw_getter, nullptr, raw_getter, this) ||
        pthread_create(&after_preproc.thread_preproc, nullptr, preproc, this) ||
        pthread_create(&to_gray.thread_to_gray, nullptr, to_gray_func, this))
        return -1;
    is_run = true;
    return 0;
}

int thread_mat::stop() {
    if (!is_run)
        return 0;
    if (pthread_cancel(raw.thread_raw_getter) || pthread_cancel(after_preproc.thread_preproc) ||
        pthread_cancel(to_gray.thread_to_gray))
        return -1;
    is_run = false;
    return 0;
}

#include <opencv2/highgui/highgui.hpp>

static void *raw_getter(void *obj) {
    auto ptr = (thread_mat *) obj;
    pthread_rwlock_wrlock(&ptr->raw.rwlock_raw);
    cv::VideoCapture cap(ptr->device);
    if (!cap.isOpened()) {
        ptr->stop();
        return nullptr;
    }
    cap.set(CV_CAP_PROP_FRAME_WIDTH, 100);
    cap.set(CV_CAP_PROP_FRAME_HEIGHT, 100);
    while (true) {
        if (ptr->raw.is_raw_data_ready)
            pthread_rwlock_wrlock(&ptr->raw.rwlock_raw);
        else
            ptr->raw.is_raw_data_ready = true;
        cap >> ptr->raw.mat_raw;
        pthread_rwlock_unlock(&ptr->raw.rwlock_raw);
    }
}

static void *preproc(void *obj) {
    auto ptr = (thread_mat *) obj;
    do {
        pthread_rwlock_rdlock(&ptr->raw.rwlock_raw);
        if (ptr->raw.is_raw_data_ready) {
            pthread_rwlock_unlock(&ptr->raw.rwlock_raw);
            break;
        }
        pthread_rwlock_unlock(&ptr->raw.rwlock_raw);
    } while (true);

    while (true) {
        pthread_rwlock_rdlock(&ptr->raw.rwlock_raw);
        pthread_rwlock_wrlock(&ptr->after_preproc.rwlock_preproc);
        cv::GaussianBlur(ptr->raw.mat_raw, ptr->after_preproc.mat_after_preproc, cv::Size(7, 7), 0, 0);
        ptr->after_preproc.is_after_pre_comp_data_ready = true;
        pthread_rwlock_unlock(&ptr->raw.rwlock_raw);
        pthread_rwlock_unlock(&ptr->after_preproc.rwlock_preproc);
    }
}

static void *to_gray_func(void *obj) {
    auto ptr = (thread_mat *) obj;
    do {
        pthread_rwlock_rdlock(&ptr->after_preproc.rwlock_preproc);
        if (ptr->after_preproc.is_after_pre_comp_data_ready) {
            pthread_rwlock_unlock(&ptr->after_preproc.rwlock_preproc);
            break;
        }
        pthread_rwlock_unlock(&ptr->after_preproc.rwlock_preproc);
    } while (true);

    while (true) {
        pthread_rwlock_rdlock(&ptr->after_preproc.rwlock_preproc);
        pthread_rwlock_wrlock(&ptr->to_gray.rwlock_to_gray);
        cv::cvtColor(ptr->after_preproc.mat_after_preproc, ptr->to_gray.mat_gray, CV_RGB2GRAY);
        ptr->to_gray.is_gray_mat_ready = true;
        pthread_rwlock_rdlock(&ptr->after_preproc.rwlock_preproc);
        pthread_rwlock_unlock(&ptr->to_gray.rwlock_to_gray);
    }
}
